package org.jeeasy.common.core.base.convert;

import com.baomidou.mybatisplus.core.metadata.IPage;
import lombok.NonNull;
import org.jeeasy.common.core.annotation.convert.BeanTransfer;
import org.mapstruct.factory.Mappers;
import org.springframework.util.CollectionUtils;

import java.io.Serializable;
import java.util.Collection;
import java.util.List;

/**
 * 基础转换类，用于DO（数据对象）和DTO（数据传输对象）之间的转换
 * 此类使用了MapStruct进行对象之间的映射，通过注解@BeanTransfer指定具体的转换器
 *
 * @param <DO> 继承自BaseDO的类型，代表数据对象
 */
public abstract class BaseTransfer<DO extends BaseDO<DO>> implements Serializable {

    /**
     * 获取当前类所关联的MapStruct转换器实例
     * 该方法利用反射获取类上的@BeanTransfer注解，并通过MapStruct的工厂方法获取转换器实例
     *
     * @return 当前类关联的MapStruct转换器实例
     */
    private BaseConverter<DO, BaseTransfer<DO>> getMapper() {
        BeanTransfer annotation = this.getClass().getAnnotation(BeanTransfer.class);
        return Mappers.getMapper(annotation.value());
    }

    /**
     * 将当前实例转换为对应的DO实例
     * 此方法利用getMapper()获取转换器，并调用其from方法进行转换
     *
     * @return 转换后的DO实例
     */
    public DO toDO() {
        return getMapper().toDO(this);
    }

    /**
     * 将集合中的每个元素转换为对应的DO实例
     * 如果源集合为空，则返回一个空集合
     * 此方法利用源集合中的第一个元素获取转换器，并调用其from方法进行批量转换
     *
     * @param source     源集合，包含待转换的DTO实例
     * @param <DO>       数据对象类型
     * @param <TRANSFER> 转换对象类型
     * @return 转换后的DO实例集合
     */
    public static <DO extends BaseDO<DO>, TRANSFER extends BaseTransfer<DO>> Collection<DO> toDO(Collection<TRANSFER> source) {
        if (CollectionUtils.isEmpty(source)) {
            return List.of();
        }
        // 获取source第一个元素
        TRANSFER transfer = source.stream().findFirst().orElse(null);
        BeanTransfer annotation = transfer.getClass().getAnnotation(BeanTransfer.class);
        BaseConverter<DO, TRANSFER> converter = Mappers.getMapper(annotation.value());
        return converter.toDO(source);
    }

    /**
     * 将集合中的每个元素转换为对应的DO实例
     * 如果源集合为空，则返回一个空集合
     * 此方法利用源集合中的第一个元素获取转换器，并调用其from方法进行批量转换
     *
     * @param source     源集合，包含待转换的DTO实例
     * @param <DO>       数据对象类型
     * @param <TRANSFER> 转换对象类型
     * @return 转换后的DO实例集合
     */
    public static <DO extends BaseDO<DO>, TRANSFER extends BaseTransfer<DO>> IPage<DO> toDO(IPage<TRANSFER> source) {
        if (CollectionUtils.isEmpty(source.getRecords())) {
            return source.convert((a) -> a.toDO());
        }
        // 获取source第一个元素
        TRANSFER transfer = source.getRecords().stream().findFirst().orElse(null);
        assert transfer != null;
        BeanTransfer annotation = transfer.getClass().getAnnotation(BeanTransfer.class);
        BaseConverter<DO, TRANSFER> converter = Mappers.getMapper(annotation.value());
        return source.convert(converter::toDO);
    }

    /**
     * 将集合中的每个元素转换为指定类型的实例
     * 如果源集合为空，则返回一个空集合
     * 此方法通过目标类的@BeanTransfer注解获取转换器，并调用其to方法进行批量转换
     *
     * @param source     源集合，包含待转换的DO实例
     * @param clazz      目标转换对象的类类型
     * @param <DO>       数据对象类型
     * @param <TRANSFER> 转换对象类型
     * @return 转换后的目标类型实例集合
     */
    public static <DO extends BaseDO<DO>, TRANSFER extends BaseTransfer<DO>> Collection<TRANSFER> transfer(Collection<DO> source, Class<TRANSFER> clazz) {
        if (CollectionUtils.isEmpty(source)) {
            return List.of();
        }
        BeanTransfer annotation = clazz.getAnnotation(BeanTransfer.class);
        BaseConverter<DO, TRANSFER> mapper = Mappers.getMapper(annotation.value());
        return mapper.transfer(source);
    }

    /**
     * 将集合中的每个元素转换为指定类型的实例
     * 如果源集合为空，则返回一个空集合
     * 此方法通过目标类的@BeanTransfer注解获取转换器，并调用其to方法进行批量转换
     *
     * @param source     源集合，包含待转换的DO实例
     * @param clazz      目标转换对象的类类型
     * @param <DO>       数据对象类型
     * @param <TRANSFER> 转换对象类型
     * @return 转换后的目标类型实例集合
     */
    public static <DO extends BaseDO<DO>, TRANSFER extends BaseTransfer<DO>> IPage<TRANSFER> transfer(@NonNull IPage<DO> source, Class<TRANSFER> clazz) {
        if (CollectionUtils.isEmpty(source.getRecords())) {
            return source.convert((a) -> a.transfer(clazz));
        }
        BeanTransfer annotation = clazz.getAnnotation(BeanTransfer.class);
        BaseConverter<DO, TRANSFER> mapper = Mappers.getMapper(annotation.value());
        return source.convert(mapper::transfer);
    }
}
